package com.mindskip.xzs.configuration.spring.security;
//这段代码是一个认证过程的关键部分
//`attemptAuthentication` 方法用于尝试用户认证，从请求中读取 JSON 数据并转换为 `AuthenticationBean` 对象，然后创建一个包含用户名和密码的认证请求 `UsernamePasswordAuthenticationToken`。
// 如果发生 IO 异常，会记录错误并创建一个空的认证请求。最后通过认证管理器处理认证请求并返回认证结果。
//`setUserDetailsService` 方法用于设置 `UserDetailsService` 用户详细信息服务，并配置记住我服务。
// 在该方法中创建了 `RestTokenBasedRememberMeServices` 对象，并设置了令牌的有效期，然后将记住我服务设置为该对象。
//`setDetails` 方法用于设置认证请求的详细信息，从请求中构建详细信息并设置到认证请求中。
//在前端，用户在登录页面输入用户名和密码后，点击登录按钮触发提交表单请求。后端接收到请求后会调用 `attemptAuthentication` 方法进行认证，验证用户身份是否合法。
// 如果认证成功，用户将被授权访问受保护的资源；如果认证失败，则会返回相应错误信息给前端。
//在后端，通过设置 `UserDetailsService` 和配置记住我服务，可以实现用户详细信息的管理和记住我功能，提升用户体验和安全性。
// 通过设置详细信息，可以记录用户认证时的相关信息，方便后续操作和日志记录。
import com.mindskip.xzs.configuration.property.CookieConfig;
import com.mindskip.xzs.utility.JsonUtil;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * RestLoginAuthenticationFilter 继承了 AbstractAuthenticationProcessingFilter
 * 用于处理 RESTful 登录认证请求
 */
public class RestLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private final org.slf4j.Logger logger = LoggerFactory.getLogger(RestLoginAuthenticationFilter.class);

    /**
     * 构造函数，设置过滤器的 URL 匹配模式为 POST 请求到 "/api/user/login"
     */
    public RestLoginAuthenticationFilter() {
        super(new AntPathRequestMatcher("/api/user/login", "POST"));
    }

    /**
     * 尝试进行身份验证
     * @param request HTTP 请求
     * @param response HTTP 响应
     * @return Authentication 封装了用户认证信息
     * @throws AuthenticationException 当认证过程中发生错误时抛出异常
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UsernamePasswordAuthenticationToken authRequest;
        try (InputStream is = request.getInputStream()) {
            // 从请求中读取 JSON 并转换为 AuthenticationBean 对象
            AuthenticationBean authenticationBean = JsonUtil.toJsonObject(is, AuthenticationBean.class);
            // 在请求中设置 "remember-me" 属性
            request.setAttribute(TokenBasedRememberMeServices.DEFAULT_PARAMETER, authenticationBean.isRemember());
            // 创建包含用户名和密码的认证请求
            authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.getUserName(), authenticationBean.getPassword());
        } catch (IOException e) {
            // 捕获并记录 IO 异常
            logger.error(e.getMessage(), e);
            // 如果发生异常，创建一个空的认证请求
            authRequest = new UsernamePasswordAuthenticationToken("", "");
        }
        // 设置认证请求的详细信息
        setDetails(request, authRequest);
        // 返回认证管理器处理后的认证结果
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    /**
     * 设置 UserDetailsService 并配置记住我服务
     * @param userDetailsService 用户详细信息服务
     */
    void setUserDetailsService(UserDetailsService userDetailsService) {
        // 创建 TokenBasedRememberMeServices 对象
        RestTokenBasedRememberMeServices tokenBasedRememberMeServices = new RestTokenBasedRememberMeServices(CookieConfig.getName(), userDetailsService);
        // 设置令牌有效期
        tokenBasedRememberMeServices.setTokenValiditySeconds(CookieConfig.getInterval());
        // 设置记住我服务
        setRememberMeServices(tokenBasedRememberMeServices);
    }

    /**
     * 设置认证请求的详细信息
     * @param request HTTP 请求
     * @param authRequest 认证请求
     */
    private void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        // 从请求中构建详细信息并设置到认证请求中
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }
}
